module cache(
  input clk,
  input rst,

  input direct,

  input invalid,

  input ren,
  input [63:0] raddr,
  input [3:0] rsize,
  output rbusy,
  output reg [63:0] rdata, 
  output rdata_valid,

  output wbusy,
  input wen,
  input [63:0] waddr,
  input [63:0] wdata, 
  input [3:0] wsize,

  output [5:0] io_sram0_addr,
  output io_sram0_cen,
  output io_sram0_wen,
  output [127:0] io_sram0_wmask,
  output [127:0] io_sram0_wdata,
  input [127:0] io_sram0_rdata,
  
  output [5:0] io_sram1_addr,
  output io_sram1_cen,
  output io_sram1_wen,
  output [127:0] io_sram1_wmask,
  output [127:0] io_sram1_wdata,
  input [127:0] io_sram1_rdata,

  output [5:0] io_sram2_addr,
  output io_sram2_cen,
  output io_sram2_wen,
  output [127:0] io_sram2_wmask,
  output [127:0] io_sram2_wdata,
  input [127:0] io_sram2_rdata,

  output [5:0] io_sram3_addr,
  output io_sram3_cen,
  output io_sram3_wen,
  output [127:0] io_sram3_wmask,
  output [127:0] io_sram3_wdata,
  input [127:0] io_sram3_rdata,

  input mem_rbusy,
  output reg mem_ren,
  output reg [3:0] mem_rsize,
  output reg [31:0] mem_raddr,
  input [63:0] mem_rdata,
  input mem_rdata_valid,
  input mem_wbusy,
  output mem_wen,
  output reg [3:0] mem_wsize,
  output reg [31:0] mem_waddr,
  output reg [63:0] mem_wdata
  

);
  reg [31:11] TAG0 [255:0]/*verilator public_flat*/;
  reg [31:11] TAG1 [255:0]/*verilator public_flat*/;
  reg [255:0] VALID_0 /*verilator public_flat*/;
  reg [255:0] VALID_1 /*verilator public_flat*/;
  reg TURN [255:0]/*verilator public_flat*/;


  wire [7:0] rindex  = raddr[10:3];
  wire [2:0] roffset = raddr[2:0];
  wire [7:0] windex  = waddr[10:3];
  wire [2:0] woffset = waddr[2:0];
  
  // wire direct = 1'b0;
  wire rdirect = ren&direct;
  wire wdirect = wen&direct;
  wire rhit0  /*verilator public_flat*/= (~direct)&ren&(~rbusy)&(TAG0[rindex] == raddr[31:11])&VALID_0[rindex];
  wire rhit1  /*verilator public_flat*/= (~direct)&ren&(~rbusy)&(TAG1[rindex] == raddr[31:11])&VALID_1[rindex];
  wire rmiss0 /*verilator public_flat*/= (~direct)&ren&(~rbusy)&((TAG0[rindex] != raddr[31:11])|(~VALID_0[rindex]));
  wire rmiss1 /*verilator public_flat*/= (~direct)&ren&(~rbusy)&((TAG1[rindex] != raddr[31:11])|(~VALID_1[rindex]));
  wire whit0  /*verilator public_flat*/= (~direct)&wen&(~wbusy)&(TAG0[windex] == waddr[31:11])&VALID_0[windex];
  wire whit1  /*verilator public_flat*/= (~direct)&wen&(~wbusy)&(TAG1[windex] == waddr[31:11])&VALID_1[windex];
  wire wmiss0 /*verilator public_flat*/= (~direct)&wen&(~wbusy)&((TAG0[windex] != waddr[31:11])|(~VALID_0[windex]));
  wire wmiss1 /*verilator public_flat*/= (~direct)&wen&(~wbusy)&((TAG1[windex] != waddr[31:11])|(~VALID_1[windex]));

  reg ram_wen;
  reg [7:0] ram_wstrb;
  reg [8:0] ram_addr;
  reg [63:0] ram_wdata;
  wire [63:0] ram_rdata;

  reg adirect;
  reg [31:0] araddr;
  reg [3:0] arsize;
  wire [7:0] mem_rindex  = araddr[10:3];
  wire [2:0] mem_roffset = araddr[2:0];

  wire [7:0] mem_windex  = mem_waddr[10:3];
  wire [2:0] mem_woffset = mem_waddr[2:0];

  localparam FSM_IDLE  = 4'b00;
  localparam FSM_READ  = 4'b01; /* Load data and allocate cacheline. */
  localparam FSM_WRITE = 4'b11; /* Store data to memory and cacheline. */
  localparam FSM_WAIT  = 4'b10; 
  reg [3:0] state;

  always @(posedge clk) begin
    if(rst) state <= FSM_IDLE; 
    else begin
      state <= state;
      case(state)
        FSM_IDLE: if((rmiss0&&rmiss1)||rdirect||(wmiss0&wmiss1)) state <= FSM_READ; else if(wdirect|whit0|whit1) state <= FSM_WRITE;
        FSM_READ: if(mem_rdata_valid) 
                    if(write_miss) state <= FSM_WRITE; 
                    else state <= FSM_IDLE; 
        FSM_WRITE: if(mem_wen&(~mem_wbusy)) state <= FSM_IDLE;
        FSM_WAIT: state <= FSM_IDLE;
        default: state <= FSM_IDLE;
      endcase
    end
  end


  reg rdata_valid_o;
  assign rdata_valid = rdata_valid_o|(mem_rdata_valid&(~write_miss));
  integer i;
  
  reg write_miss;
  always @(posedge clk) begin
    if(rst) begin
      VALID_0 <= 256'b0;
      VALID_1 <= 256'b0;
      rdata_valid_o <= 1'b0;
      mem_raddr <= 32'b0;
      mem_waddr <= 32'b0;
      mem_ren <= 1'b0;
      mem_wen <= 1'b0;
      mem_rsize <= 4'b0;
      mem_wsize <= 4'b0;
      mem_wdata <= 64'b0;
      araddr <= 32'b0;
      arsize <= 4'b0;
      write_miss <= 1'b0;
      adirect <= 1'b0;
    end else begin
      if(invalid) begin
        VALID_0 <= 256'b0;
        VALID_1 <= 256'b0;
      end
      rdata_valid_o <= 1'b0;
      case(state)
        FSM_IDLE: begin
          mem_wen <= 1'b0;
          mem_ren <= 1'b0;
          adirect <= direct;
          rdata_valid_o <= rhit0|rhit1;
          araddr <= raddr[31:0];
          write_miss <= wmiss0&wmiss1;
          if(direct) begin 
            arsize <= rsize;
            mem_rsize <= rsize;
            mem_raddr <= raddr[31:0];
          end else begin
            arsize <= rsize;
            mem_rsize <= 4'b1000;
            mem_raddr <= {raddr[31:3],3'b0};
          end

          if(rhit0|whit0) TURN[rindex] <= 1'b1;
          else if(rhit1|whit1) TURN[rindex] <= 1'b0;
          /* If read or write access is miss, load data from memory. */
          if((rmiss0&&rmiss1)||rdirect||(wmiss0&wmiss1)) begin
            mem_ren <= 1'b1;
          end
          if(whit0|whit1|wdirect) begin
            mem_wen <= wdirect;
          end
          mem_wsize <= wsize;
          mem_waddr <= waddr[31:0];
          case(wsize)
          4'b0001: begin mem_wdata <= {8{wdata[0+: 8]}}; end
          4'b0010: begin mem_wdata <= {4{wdata[0+:16]}}; end
          4'b0100: begin mem_wdata <= {2{wdata[0+:32]}}; end
          4'b1000: begin mem_wdata <= {1{wdata[0+:64]}}; end
          default: begin mem_wdata <= 64'b0; end
          endcase 
        end
        FSM_READ: begin
          if(mem_ren&(~mem_rbusy)) mem_ren <= 1'b0;
          if(mem_rdata_valid&~adirect&~write_miss) begin
            if(TURN[mem_rindex] == 1'b0) begin
              TURN[mem_rindex] <= 1'b1;
              VALID_0[mem_rindex] <= 1'b1;
              TAG0[mem_rindex] <= mem_raddr[31:11];
            end else begin
              TURN[mem_rindex] <= 1'b0;
              VALID_1[mem_rindex] <= 1'b1;
              TAG1[mem_rindex] <= mem_raddr[31:11];
            end 
          end
          if(mem_rdata_valid&~adirect&write_miss)
          begin
            if(TURN[mem_windex] == 1'b0) begin
              TURN[mem_windex] <= 1'b1;
              VALID_0[mem_windex] <= 1'b1;
              TAG0[mem_windex] <= mem_waddr[31:11];
            end else begin
              TURN[mem_windex] <= 1'b0;
              VALID_1[mem_windex] <= 1'b1;
              TAG1[mem_windex] <= mem_waddr[31:11];
            end 
            mem_wdata <= mem_rdata;
            mem_wen <= 1'b1;
            mem_wsize <= 4'b1000;
            mem_waddr[2:0] <= 3'b0;
            case(mem_wsize)
            4'b0001: mem_wdata[{mem_waddr[2:0],3'b0}+: 8] <= mem_wdata[0+: 8];
            4'b0010: mem_wdata[{mem_waddr[2:1],4'b0}+:16] <= mem_wdata[0+:16];
            4'b0100: mem_wdata[{mem_waddr[2:2],5'b0}+:32] <= mem_wdata[0+:32];
            4'b1000: mem_wdata <= mem_wdata;
            default: ;
            endcase
          end
        end
        FSM_WRITE: begin
          
          if(~mem_wen) begin
            mem_wdata <= ram_rdata;
            mem_wen <= 1'b1;
            mem_wsize <= 4'b1000;
            mem_waddr[2:0] <= 3'b0;
            case(mem_wsize)
            4'b0001: mem_wdata[{mem_waddr[2:0],3'b0}+: 8] <= mem_wdata[0+: 8];
            4'b0010: mem_wdata[{mem_waddr[2:1],4'b0}+:16] <= mem_wdata[0+:16];
            4'b0100: mem_wdata[{mem_waddr[2:2],5'b0}+:32] <= mem_wdata[0+:32];
            4'b1000: mem_wdata <= mem_wdata;
            default: ;
            endcase
          end
          /* Data has send to BUS. */
          if(mem_wen&(~mem_wbusy)) begin 
            mem_wen <= 1'b0;
            write_miss <= 1'b0;
          end
        end
        FSM_WAIT: begin
          
        end
        default: begin
          
        end
      endcase
    end
  end

  
  always @(*) begin
    ram_wen = 1'b0;
    ram_wstrb = 8'h0;
    ram_addr = 9'b0;
    ram_wdata = 64'b0;
    case(state)
    FSM_IDLE: begin
      ram_wen = 1'b0;
      ram_wstrb = 8'h0;
      if(rhit0|rhit1)
        ram_addr = {rhit1,rindex};
      else if(whit0|whit1)
        ram_addr = {whit1,windex};
      ram_wdata = 64'b0;
    end
    FSM_READ: begin 
      if(mem_rdata_valid&(~adirect)) begin
        ram_wen = 1'b1;
        ram_wstrb = 8'hff;
        if(write_miss) ram_addr = {TURN[mem_windex],mem_windex};
        else ram_addr = {TURN[mem_rindex],mem_rindex};
        ram_wdata = mem_rdata;
      end
    end
    FSM_WRITE: begin
      case(mem_wsize)
      4'b0001: begin ram_wdata = {8{mem_wdata[0+: 8]}}; ram_wstrb = 8'b1  << mem_woffset[2:0]; end
      4'b0010: begin ram_wdata = {4{mem_wdata[0+:16]}}; ram_wstrb = 8'b11 << {mem_woffset[2:1],1'b0}; end
      4'b0100: begin ram_wdata = {2{mem_wdata[0+:32]}}; ram_wstrb = 8'hf  << {mem_woffset[2:2],2'b0}; end
      4'b1000: begin ram_wdata = {1{mem_wdata[0+:64]}}; ram_wstrb = 8'hff; end
      default: begin ram_wdata = 64'b0; ram_wstrb = 8'b0; end
      endcase  
      if(mem_wen&(~mem_wbusy)&(~adirect)) begin
        if(TAG0[mem_windex] == mem_waddr[31:11]&&VALID_0[mem_windex]) begin
          ram_wen = 1'b1;
          ram_addr = {1'b0,mem_windex};
        end else if(TAG1[mem_windex] == mem_waddr[31:11]&&VALID_1[mem_windex]) begin
          ram_wen = 1'b1;
          ram_addr = {1'b1,mem_windex};
        end
      end
    end
    default: ;
    endcase
  end

  always @(*) begin
    rdata = 64'b0;
    case(state)
    FSM_IDLE: begin
      case(arsize)
      4'b0001: rdata[ 7:0] = ram_rdata[{mem_roffset[2:0],3'b0}+: 8];
      4'b0010: rdata[15:0] = ram_rdata[{mem_roffset[2:1],4'b0}+:16];
      4'b0100: rdata[31:0] = ram_rdata[{mem_roffset[2:2],5'b0}+:32];
      4'b1000: rdata = ram_rdata;
      default: rdata = 64'h0;
      endcase
    end
    FSM_READ: begin 
      case(arsize)
      4'b0001: rdata[ 7:0] = mem_rdata[{mem_roffset[2:0],3'b0}+: 8];
      4'b0010: rdata[15:0] = mem_rdata[{mem_roffset[2:1],4'b0}+:16];
      4'b0100: rdata[31:0] = mem_rdata[{mem_roffset[2:2],5'b0}+:32];
      4'b1000: rdata = mem_rdata;
      default: rdata = 64'h0;
      endcase
    end
    default: rdata = 64'h0;
    endcase
  end

  sram4K inst_sram4K(
    .clk(clk),
    .rst(rst),
    .io_sram0_addr(io_sram0_addr),
    .io_sram0_cen(io_sram0_cen),
    .io_sram0_wen(io_sram0_wen),
    .io_sram0_wmask(io_sram0_wmask),
    .io_sram0_wdata(io_sram0_wdata),
    .io_sram0_rdata(io_sram0_rdata),
    .io_sram1_addr(io_sram1_addr),
    .io_sram1_cen(io_sram1_cen),
    .io_sram1_wen(io_sram1_wen),
    .io_sram1_wmask(io_sram1_wmask),
    .io_sram1_wdata(io_sram1_wdata),
    .io_sram1_rdata(io_sram1_rdata),
    .io_sram2_addr(io_sram2_addr),
    .io_sram2_cen(io_sram2_cen),
    .io_sram2_wen(io_sram2_wen),
    .io_sram2_wmask(io_sram2_wmask),
    .io_sram2_wdata(io_sram2_wdata),
    .io_sram2_rdata(io_sram2_rdata),
    .io_sram3_addr(io_sram3_addr),
    .io_sram3_cen(io_sram3_cen),
    .io_sram3_wen(io_sram3_wen),
    .io_sram3_wmask(io_sram3_wmask),
    .io_sram3_wdata(io_sram3_wdata),
    .io_sram3_rdata(io_sram3_rdata),
    .wen(ram_wen),
    .wstrb(ram_wstrb),
    .wdata(ram_wdata),
    .addr(ram_addr),
    .rdata(ram_rdata)
  );
  
  wire busy = (state != FSM_IDLE) ? 1'b1 : 1'b0;
  assign rbusy = busy;
  assign wbusy = busy;
endmodule
